package com.tfswx.jcyw.print;

import com.tfswx.jcyw.print.bean.*;
import com.tfswx.jcyw.print.config.FileMap;
import com.tfswx.jcyw.print.config.Ggpz;
import com.tfswx.jcyw.print.listview.CustomListCell;
import com.tfswx.jcyw.print.util.CmdUtil;
import com.tfswx.jcyw.print.util.HttpUtil;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

import javax.print.PrintService;
import javax.swing.filechooser.FileSystemView;
import java.awt.*;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterAbortException;
import java.awt.print.PrinterJob;
import java.io.*;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.tfswx.jcyw.print.bean.FileItem.EXT_LIST;


/**
 * @author ricky
 * @date 2021/8/3
 */
@Slf4j
public class Sample {
    public ComboBox<PaperItem> zhizhangCombox;
    public RadioButton zongxiang;
    public RadioButton hengxiang;
    public DialogPane printProcess;
    public DialogPane downloadProcess;
    public Label percentProcess;
    public Label downloadPercentProcess;
    public ProgressBar process;
    public ProgressBar downProcess;
    public Label yeshu;
    public Label downloadmx;
    public ImageView winIcon;
    @FXML
    private ListView<FileItem> fileListView;
    @FXML
    private CheckBox allSlectCheckbox;
    @FXML
    private Label zongjiLabel;
    @FXML
    private ComboBox<PrinterItem> dayinjiCombox;
    public Button deleteBtn;
    public Button printBtn;
    public Button piliangBtn;
    public Label winTitle;
    public Button winCloseBtn;
    private static final FileMap<String> pzxx = new FileMap<>(new File(Ggpz.BASE_DIR, "pzxx.txt"));

    private final PrintObj printObj = new PrintObj();
    private final DownloadObj downloadObj = new DownloadObj();

    private final ChangeListener<Boolean> changeListener = (observable, oldValue, newValue) -> updateSelectCount();

    static {
        String mldz = pzxx.get("mldz");
        if (StringUtils.isBlank(mldz)) {
            File desktopDir = getDesktopDir();
            try {
                pzxx.put("mldz", desktopDir.getCanonicalPath());
            } catch (IOException e) {
                pzxx.put("mldz", desktopDir.getPath());
            }
        }
    }

    private static File getDesktopDir() {
        FileSystemView fileSystemView = FileSystemView.getFileSystemView();
        return fileSystemView.getHomeDirectory();
    }

    private void updateSelectCount() {
        ObservableList<FileItem> data = fileListView.getItems();
        int total = data.size();
//        log.info("获取总数：" + total);
        List<FileItem> selectItems = data.stream().filter(FileItem::isSelect).collect(Collectors.toList());
        int selectCount = selectItems.size();
//        log.info("获取选中数量：" + selectCount);
//        log.info("判断是不是全部选中：" + (total == selectCount));
        allSlectCheckbox.setSelected(total == selectCount);

        allSlectCheckbox.setText("全选(" + selectCount + "/" + total + ")");

        int cnt = selectItems.stream().mapToInt(x -> x.getPageCount().get()).sum();
        zongjiLabel.setText("总计：文档 " + selectCount + " 个，页码 " + cnt + " 页，纸张 " + cnt + " 张");

        deleteBtn.setDisable(selectCount == 0);
        printBtn.setDisable(selectCount == 0);

        printObj.setTotal(cnt);
    }

    @FXML
    void initialize() {
        winTitle.setText(Client.getWinTitle());
        winIcon.setImage(Client.getWinIcon());

        fileListView.getItems().addListener((ListChangeListener<FileItem>) c -> updateSelectCount());
        Consumer<FileItem> deleteClick = fileItem -> fileListView.getItems().removeIf(x -> x.getFile().equals(fileItem.getFile()));
        BiConsumer<FileItem, MouseEvent> itemClick = (fileItem, event) -> {
            if (event.getClickCount() == 2 && event.getButton() == MouseButton.PRIMARY) {
                FileItem selectedItem = fileListView.getSelectionModel().getSelectedItem();
                if (selectedItem == null || !Desktop.isDesktopSupported()) {
                    return;
                }
                try {
                    Desktop.getDesktop().open(selectedItem.getFile());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        fileListView.setCellFactory(param -> new CustomListCell(deleteClick, itemClick));
        fileListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue == null) {
                return;
            }
            hengxiang.setSelected(newValue.getOrientation() == PageFormat.LANDSCAPE);
            zongxiang.setSelected(!hengxiang.isSelected());
        });
        fileListView.setOnDragOver(event -> event.acceptTransferModes(TransferMode.COPY_OR_MOVE));
        fileListView.setOnDragDropped(event -> {
            Dragboard dragboard = event.getDragboard();
            List<File> files = dragboard.getFiles();
            for (File file : files) {
                addFile(file);
            }
        });

        PrintService[] printServices = PrinterJob.lookupPrintServices();
        PrintService defaultPrint = PrinterJob.getPrinterJob().getPrintService();

        for (PrintService printService : printServices) {
            PrinterItem printerItem = new PrinterItem(printService);
            dayinjiCombox.getItems().add(printerItem);
            if (defaultPrint != null && defaultPrint.getName().equals(printService.getName())) {
                dayinjiCombox.setValue(printerItem);
            }
        }
        PaperItem A4 = new PaperItem(javafx.print.Paper.A4);
        PaperItem A5 = new PaperItem(javafx.print.Paper.A5);
        zhizhangCombox.getItems().addAll(A4, A5);
        zhizhangCombox.setValue(A5);


        winCloseBtn.onMouseMovedProperty().set(event -> winCloseBtn.setStyle("-fx-background-image: url(\"image/close-hover.png\");  -fx-background-repeat: no-repeat; -fx-background-position: center,center; -fx-background-color: #f00;"));
        winCloseBtn.onMouseExitedProperty().set(event -> winCloseBtn.setStyle("-fx-background-image: url(\"image/close.png\"); -fx-background-repeat: no-repeat; -fx-background-position: center,center; -fx-background-color: transparent;"));


        deleteBtn.onMouseMovedProperty().set(event -> deleteBtn.setStyle("-fx-background-image: url(\"image/delete-hover.png\"); -fx-background-size: 100%,100%; -fx-background-repeat: no-repeat; -fx-background-position: center,center; -fx-background-color: transparent;"));
        deleteBtn.onMouseExitedProperty().set(event -> deleteBtn.setStyle("-fx-background-image: url(\"image/delete.png\"); -fx-background-size: 100%,100%; -fx-background-repeat: no-repeat; -fx-background-position: center,center; -fx-background-color: transparent;"));


        printBtn.onMouseMovedProperty().set(event -> printBtn.setStyle("-fx-background-color: #f88d7f; -fx-text-fill: #fff;"));
        printBtn.onMouseExitedProperty().set(event -> printBtn.setStyle("-fx-background-color: #ff7665; -fx-text-fill: #fff;"));

        printProcess.visibleProperty().bind(printObj.getVisibleProperty());
        yeshu.textProperty().bind(printObj.getYeshuProperty());
        percentProcess.textProperty().bind(printObj.getPercentProcessProperty());
        process.progressProperty().bind(printObj.getProcessProperty());

        downloadProcess.visibleProperty().bind(downloadObj.getVisibleProperty());
        downloadmx.textProperty().bind(downloadObj.getDoanloadMxProperty());
        downloadPercentProcess.textProperty().bind(downloadObj.getPercentProcessProperty());
        downProcess.progressProperty().bind(downloadObj.getProcessProperty());
    }

    @SneakyThrows
    public void addFileList(MouseEvent mouseEvent) {

        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("选择需要打印的文件");
        fileChooser.setInitialDirectory(getDesktopDir());
        try {
            File file = new File(pzxx.get("mldz"));
            if (file.exists()) {
                fileChooser.setInitialDirectory(file);
            }
        } catch (Exception ignored) {
        }

        FileChooser.ExtensionFilter pdf = new FileChooser.ExtensionFilter("pdf文件", "*.pdf");
        FileChooser.ExtensionFilter image = new FileChooser.ExtensionFilter("图片文件", "*.jpg", "*.jpeg", "*.png");

        List<String> list = new ArrayList<>();
        list.addAll(pdf.getExtensions());
        list.addAll(image.getExtensions());

        fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("所有文件", list), pdf, image);
        List<File> files = fileChooser.showOpenMultipleDialog(null);
        if (files == null) {
            // 没有选择文件
            return;
        }
        for (File file : files) {
            addFile(file);
        }
        pzxx.put("mldz", files.get(0).getParentFile().getCanonicalPath());
    }

    public void addFile(File file) {
        if (!file.exists()) {
            return;
        }
        if (file.isFile()) {
            if (!EXT_LIST.contains(FilenameUtils.getExtension(file.getName()).toUpperCase())) {
                return;
            }
            ObservableList<FileItem> items = fileListView.getItems();
            boolean hasFile = items.stream().anyMatch(x -> x.getFile().equals(file));
            if (hasFile) {
                return;
            }
            FileItem fileItem = new FileItem(file);
            fileItem.getSelectProperty().addListener(changeListener);
            items.add(fileItem);
//            updateSelectCount();
        } else if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files == null) {
                return;
            }
            for (File listFile : files) {
                addFile(listFile);
            }
        }
    }

    public void allSelectClick(MouseEvent mouseEvent) {
        if (allSlectCheckbox.isSelected()) {
            for (FileItem item : fileListView.getItems()) {
                item.setSelect(true);
            }
        } else {
            for (FileItem item : fileListView.getItems()) {
                item.setSelect(false);
            }
        }
    }

    @SneakyThrows
    public void printBtnClick(MouseEvent mouseEvent) {
        printObj.startPrint();
        ObservableList<FileItem> datas = fileListView.getItems();
        List<FileItem> fileList = datas.stream().filter(FileItem::isSelect).collect(Collectors.toList());
        PrinterItem printerItem = dayinjiCombox.getValue();

        PrintService ps = printerItem.getPrintService();

        PaperItem paperItem = zhizhangCombox.getValue();

        // 纸张
        Paper paper = new Paper();
        paper.setSize(paperItem.getWidth(), paperItem.getHeight());
        paper.setImageableArea(10, 10, paper.getWidth() - 20, paper.getHeight() - 20);

        // 格式
        PageFormat pageFormat = new PageFormat();
//        pageFormat.setOrientation(PageFormat.LANDSCAPE);
        pageFormat.setPaper(paper);

        int cancelYs = 0;
        for (FileItem fileItem : fileList) {
            if (!printObj.isPrint()) {
                log.info("已经取消打印了");
                break;
            }

            try {
                fileItem.print(ps, pageFormat);
            } catch (PrinterAbortException e) {
                log.info("取消打印");
                cancelYs += fileItem.getPageCount().get();
            } finally {
                printObj.print(fileItem.getPageCount().get());
            }

        }
        printObj.completePrint();
        Alert alert = new Alert(Alert.AlertType.NONE);
        alert.setTitle("打印结束");
        alert.getButtonTypes().add(new ButtonType("确定"));

        String msg = "打印总页数：" + (printObj.getTotalCountProperty().get() - cancelYs);
        if (cancelYs > 0) {
            msg += "\n取消页数：" + cancelYs;
        }

        alert.setContentText(msg);
        alert.showAndWait();
    }

    public void deleteFileList(MouseEvent mouseEvent) {
        fileListView.getItems().removeIf(FileItem::isSelect);
    }

    public void cancelPrint(MouseEvent mouseEvent) {
        printObj.cancelPrint();
    }

    public void closeWin(MouseEvent mouseEvent) {
        Client.close();
    }


    private double winX;
    private double winY;
    private double mouseX;
    private double mouseY;

    public void mouseDown(MouseEvent mouseEvent) {
        Stage primaryStage = Client.getPrimaryStage();
        winX = primaryStage.getX();
        winY = primaryStage.getY();
        mouseX = mouseEvent.getScreenX();
        mouseY = mouseEvent.getScreenY();
    }

    public void mouswMove(MouseEvent mouseEvent) {
        double screenX = mouseEvent.getScreenX();
        double screenY = mouseEvent.getScreenY();
        Stage primaryStage = Client.getPrimaryStage();
        primaryStage.setX(winX + screenX - mouseX);
        primaryStage.setY(winY + screenY - mouseY);
    }

    public void checkVersion(MouseEvent mouseEvent) {
        String url = "https://gitee.com/api/v5/repos/jiang516/print-client/releases?direction=desc";
        Alert alert = new Alert(Alert.AlertType.NONE);
        alert.setTitle("检测版本");
        alert.getButtonTypes().add(new ButtonType("确定", ButtonBar.ButtonData.OK_DONE));
        List<Release> releaseList;
        try {
            releaseList = HttpUtil.getForList(url, Release.class);
        } catch (Exception e) {
            log.error("检查失败", e);
            alert.setContentText("检查失败：" + e.getMessage());
            alert.show();
            return;
        }
        if (releaseList.isEmpty()) {
            alert.setContentText("没有检查到新版本");
            alert.show();
            return;
        }
        Release release = releaseList.get(0);
        String ver = release.getTag_name();
        String verStr = Client.getVer();
        if (verStr.equals(ver)) {
            alert.setContentText("没有检查到新版本");
            alert.show();
            return;
        }
        alert.getButtonTypes().add(new ButtonType("取消", ButtonBar.ButtonData.CANCEL_CLOSE));
        alert.setContentText("版本号：" + ver + "\n版本：" + release.getName() + "\n更新描述：\n" + release.getBody() + "\n是否更新？");
        Optional<ButtonType> buttonType = alert.showAndWait();
        if (!buttonType.isPresent()) {
            return;
        }
        if (buttonType.get().getButtonData() != ButtonBar.ButtonData.OK_DONE) {
            return;
        }
        // 开始下载安装包
        try {
            Release.Asset asset = release.getAssets()
                    .stream()
                    .filter(x -> StringUtils.isNotBlank(x.getName()) && x.getName().endsWith(".exe"))
                    .findFirst()
                    .orElseThrow((Supplier<Throwable>) () -> new RuntimeException("没有找到可执行文件"));
            File file = new File(Ggpz.AZB_DIR, asset.getName());
            String downloadUrl = URLDecoder.decode(asset.getBrowser_download_url(), "UTF-8");
            downloadObj.startDownload();
            new Thread(() -> {
                try {
                    HttpUtil.downloadFile(downloadUrl, file, (loaded, total) -> {
                        log.info("下载进度：" + loaded + "/" + total);
                        Platform.runLater(() -> {
                            downloadObj.updateProgress(loaded, total);
                        });
                    }, downloadObj.getStop());
                    Platform.runLater(downloadObj::completeDownload);
                    log.info("下载完成");
                    // 下载完成，开始安装
                    new Thread(() -> {
                        try {
                            CmdUtil.exec("start \"\" \"" + file.getCanonicalPath() + "\" /SILENT");
                        } catch (IOException | InterruptedException e) {
                            log.error("安装失败", e);
                        }
                    }).start();
                    Platform.runLater(Client::close);
                } catch (Exception e) {
                    log.error("下载失败", e);
                }
            }).start();

        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    public void cancelDownload(MouseEvent mouseEvent) {
        downloadObj.cancelDownload();
    }
}

